package pe.edu.sistemas.sisdiho.util;

import java.util.Date;
import java.util.Calendar;

public class Fecha {
	public final int DATE = 0;
	public final int SQL_DATE = 1;
	public final int SQL_TIME = 2;
    private final static long DAY_MILLIS = 86400000L;
    private final static long MILLIS = 18000000L;   //-> milisegundos de 1970-01-01
    private final Calendar calendar;
    
    private int act;
    
    public Fecha(Date date, int act) {
        calendar = Calendar.getInstance();
        this.act = DATE;
        switch(act){
            case SQL_DATE:
                calendar.setTimeInMillis(calculateDateMillis(date.getTime()));
                this.act = SQL_DATE;
                break;
            case SQL_TIME:
                calendar.setTimeInMillis(calculateTimeMillis(date.getTime()));
                this.act = SQL_TIME;
                break;
            default:
                calendar.setTime(date);
        }
    }
    
    public Fecha(Date date) {
        calendar = Calendar.getInstance();
        set(date);
    }
    
    public Fecha(long time, int act){
        calendar = Calendar.getInstance();        
        switch(act){
            case SQL_DATE:
                calendar.setTimeInMillis(calculateDateMillis(time));
                this.act = SQL_DATE;
                break;
            case SQL_TIME:
                calendar.setTimeInMillis(calculateTimeMillis(time));
                this.act = SQL_TIME;
                break;
            default:
                calendar.setTimeInMillis(time);
                this.act = DATE;
        }
    }
    
    public Fecha(long time){
        calendar = Calendar.getInstance();
        calendar.setTimeInMillis(time);
        act = DATE;
    }
    
    public Fecha(int year, int month, int date){
    	this(year, month, date, 0, 0);
    }
    
    public Fecha(int year, int month, int date, int hourOfDay, int minute){
    	this(year, month, date, hourOfDay, minute, 0);
    }
    
    public Fecha(int year, int month, int date, int hourOfDay, int minute, int second){
    	calendar = Calendar.getInstance();
    	calendar.set(year, month, date, hourOfDay, minute, second);
    	act = DATE;
    }
 
    public Date get(){
    	return calendar.getTime();
    }
    
    public void set(Date date){
    	if(date instanceof java.sql.Date){
            calendar.setTimeInMillis(calculateDateMillis(date.getTime()));
            act = SQL_DATE;
    	}else if(date instanceof java.sql.Time){
            calendar.setTimeInMillis(calculateTimeMillis(date.getTime()));
            act = SQL_TIME;
    	}else{
            calendar.setTime(date);
            act = DATE;
    	}
    }
    
    public int act(){
    	return act;
    }
    /**
     * Cambian los milisegundos de Fecha para adecuarse a su comportamiento 
     * futuro.
     * Importante:	Los cambios son irreversibles pudiendo alterar algunos
     * datos al pasar de uno a otro. Solo se recomienda usar cuando se pasa de
     * util Date a sql Date por que solo se mantienen la fecha; y tambien de
     * util Date a sql Time por que solo se mantienen el tiempo.
     * @param act como sera el comportamiento 1 para SQL Date, 2 para SQL Time
     * y 0 u otro numero para UTIL Date
     */
    public void actAs(int act){
    	if(this.act == act)	return;
    	switch(act){
	        case SQL_DATE:
	            calendar.setTimeInMillis(calculateDateMillis(getTime()));
	            this.act = SQL_DATE;
	            break;
	        case SQL_TIME:
	            calendar.setTimeInMillis(calculateTimeMillis(getTime()));
	            this.act = SQL_TIME;
	            break;
	        default:
	        	this.act = DATE;
    	}
    }
    
    public java.sql.Date sqlDate(){
    	return new java.sql.Date(calculateDateMillis(getTime()));
    }
    
    public java.sql.Time sqlTime(){
    	return new java.sql.Time(calculateTimeMillis(getTime())); 
    }
    
    public long getTime(){
        return calendar.getTimeInMillis();
    }
    
    public void setTime(long millis){
        calendar.setTimeInMillis(millis);
    }
    /**
     * Al igual que <code>java.util.Date</code> que establece que 
     * 0 representa a domingo, 1 a lunes y asi sucesivamente.
     * @return 0 si es domingo ~ 6 si es sabado
     */
    public int getDay(){
        return calendar.get(Calendar.DAY_OF_WEEK) - 1;
    }
    
    public int getDate(){
        return calendar.get(Calendar.DATE);
    }
    
    public void setDate(int value){
        calendar.set(Calendar.DATE, value);
    }
    
    public int getMonth(){
        return calendar.get(Calendar.MONTH);
    }
    
    public void setMonth(int value){
        calendar.set(Calendar.MONTH, value);
    }
    
    public int getYear(){
        return calendar.get(Calendar.YEAR);
    }
    
    public void setYear(int value){
        calendar.set(Calendar.YEAR, value);
    }
    
    public int getHours(){
        return calendar.get(Calendar.HOUR_OF_DAY);
    }
    
    public void setHours(int value){
        calendar.set(Calendar.HOUR_OF_DAY, value);
    }
    
    public int getMinutes(){
        return calendar.get(Calendar.MINUTE);
    }
    
    public void setMinutes(int value){
        calendar.set(Calendar.MINUTE, value);
    }
    
    public int getSeconds(){
        return calendar.get(Calendar.SECOND);
    }
    
    public void setSeconds(int value){
        calendar.set(Calendar.SECOND, value);
    }
    
    public int compareTo(Fecha fecha){
    	return equals(fecha) ? 0 : this.after(fecha) ? 1 : -1;
    }
    
    public boolean after(Fecha fecha){
    	return fecha.getTime() < this.getTime();
    }
    
    public boolean before(Fecha fecha){
    	return this.getTime() < fecha.getTime();
    }
    
    public boolean equals(Fecha fecha){
        return this.getTime() == fecha.getTime();
    }
    
    public Fecha clone(){
    	return new Fecha(calendar.getTimeInMillis(), act);
    }
    
    public String toString(){
    	switch(act){
    		case SQL_DATE:	return sqlDate().toString();
    		case SQL_TIME:	return sqlTime().toString();
    		default:		return get().toString();
    	}
    }
    /**
     * Crea un objeto <code>Fecha</code> con la fecha y el tiempo actual.
     * @return <code>Fecha</code> con la fecha y el tiempo actual.
     */
    public static Fecha now(){
        return new Fecha(System.currentTimeMillis());
    }
    /**
     * Crea un objeto <code>Fecha</code> con la fecha actual pero no el tiempo.
     * @return <code>Fecha</code> con la fecha actual.
     */
    public static Fecha nowToday(){
        return new Fecha(calculateDateMillis(System.currentTimeMillis()));
    }
    /**
     * Crea un objeto <code>Fecha</code> con el tiempo actual pero no la fecha.
     * @return <code>Fecha</code> con el tiempo actual.
     */
    public static Fecha nowTime(){
        return new Fecha(calculateTimeMillis(System.currentTimeMillis()));
    }
    /**
     * Crea un objeto <code>java.sql.Date</code> con la fecha actual.
     * @return <code>java.sql.Date</code> con la fecha actual.
     */
    public static java.sql.Date today() {
        return new java.sql.Date(calculateDateMillis(System.currentTimeMillis()));
    }
    /**
     * Crea un objeto <code>java.sql.Time</code> con el tiempo actual.
     * @return <code>java.sql.Time</code> con el tiempo actual.
     */
    public static java.sql.Time time() {
        return new java.sql.Time(calculateTimeMillis(System.currentTimeMillis()));
    }
    /**
     * Utiliza los milisegundos de <code>date</code> para objener un objeto de
     * la clase <code>java.sql.Date</code>.
     * @param date <code>java.util.Date</code> de referencia
     * @return <code>java.sql.Date</code> solo con las fechas semejantes a
     * <code>date</code>
     */
    public static java.sql.Date toSQLDate(Date date) {
        return new java.sql.Date(calculateDateMillis(date.getTime()));
    }
    /**
     * Utiliza los milisegundos de <code>date</code> para objener un objeto de
     * la clase <code>java.sql.Time</code>.
     * @param date <code>java.util.Date</code> de referencia
     * @return <code>java.sql.Time</code> solo con los tiempos semejantes a
     * <code>date</code>
     */
    public static java.sql.Time toSQLTime(Date date) {
        return new java.sql.Time(calculateTimeMillis(date.getTime()));
    }
    /**
     * Toma los milisegundos de referencia y lo adapta para que se solo 
     * tenga en cuenta la fecha, mas no el tiempo.
     * @param millis milisegundos de referencia
     * @return milisegundos correspondientes al siguiente formato Date
     * yyyy-MM-dd 00:00:00.000
     */
    public static long calculateDateMillis(long millis){
        return MILLIS + ((millis - MILLIS)/DAY_MILLIS)*DAY_MILLIS;
    }
    /**
     * Toma los milisegundos de referencia y lo adapta para que se solo 
     * tenga en cuenta el tiempo, mas no la fecha.
     * @param millis milisegundos de referencia
     * @return milisegundos correspondientes al siguiente formato Time
     * 1970-01-01 hh:mm:ss.000, exactamente con "0" milisegundos
     */
    public static long calculateTimeMillis(long millis){
    	return MILLIS + (((millis - MILLIS)%DAY_MILLIS)/1000)*1000;
    }
}